#include <assert.h>
#include <map>
#include <vector>
#include <time.h>
#include <GL/glut.h>
#include <iostream>

#include "SceneManager.hpp"
#include "Entities/BaseEntity.hpp"
#include "Entities/Entity3D.hpp"
#include "Entities/LevelSector.hpp"
#include "ScreenSystem/ScreenManager.hpp"
#include "ScreenSystem/DemoSceneScreen.hpp"
#include "HDRDemoConstants.hpp"
#include "Camera/CameraManager.hpp"
#include "Camera/CameraObject.hpp"
#include "Camera/ViewFrustum.hpp"
#include "Mathematics/MMatrix4.hpp"
#include "Particles/EmitterManager.hpp"
#include "Collisions/BoundingSphere.hpp"

//! debug
#include "ResourceManagers/TextureManager.hpp"
#include "ResourceManagers/TextureObject.hpp"
#include "Globals.hpp"

using namespace std;

//! Setting values for static variables.
SceneManager*            SceneManager::m_instance = 0;


//! GetInstance method returns pointer to SceneManager instance
//! and ensures that only one such instance exists.
SceneManager* SceneManager::GetInstance()
{
	if( !m_instance )
	{
		m_instance = new SceneManager;
	}

	return m_instance;
}

SceneManager::SceneManager()
{
	m_startTick     = clock();
	m_lastFrameTick = m_startTick;
	m_idIndex       = 0;
	m_numOfFrames   = 0;
	m_pPlayer       = 0;
}

//! Method registering entity with its unique ID number.
void SceneManager::RegisterEntity( BaseEntity* newEntity )
{
	// Check if entity exists.
	assert( newEntity );

	// Register entity with unique ID.
	newEntity->setEntityID( GenerateUniqueID() );

	m_entitiesMap[newEntity->getEntityID()] = newEntity;
}

//! Here all entities are unregistered and destroyed.
void SceneManager::UnregisterAllEntities()
{
	map<int, BaseEntity*>::iterator mapBegin = m_entitiesMap.begin();
	map<int, BaseEntity*>::iterator mapEnd   = m_entitiesMap.end();

	for( mapBegin; mapBegin != mapEnd; ++mapBegin )
	{
		delete mapBegin->second;
		//m_entitiesToDestroy.push_back( mapBegin->first );
	}
	m_entitiesMap.clear();


/*
	vector<int>::iterator beginOfUnregistered = m_entitiesToDestroy.begin();
	vector<int>::iterator endOfUnregistered   = m_entitiesToDestroy.end();

	// Destroy unregistered entities.
	for( beginOfUnregistered; beginOfUnregistered != endOfUnregistered; ++ beginOfUnregistered )
	{
		delete m_entitiesMap[ *beginOfUnregistered ];
		m_entitiesMap[ *beginOfUnregistered ] = NULL;
	}
	m_entitiesMap.clear();*/
}

//! Unregistered entity will be destroyed on next UpdateScene() call.
void SceneManager::UnregisterEntity(int entityID)
{
	m_entitiesToDestroy.push_back( entityID );
}

//! Method updating whole scene. First step is to destroy unregistered entities.
//! Second is to update state of registered entities. Third step is to render all
//! registered entities.
void SceneManager::UpdateScene( clock_t tick )
{
	/////////////////////// FPS etc ///////////////////////////////////////////////
	++m_numOfFrames;
	clock_t delta = tick - m_lastFrameTick;
	m_lastFrameTick = tick;

	if( delta <= 0 )
		return;

	if( !((tick - m_startTick) % 2000) )
	{
		std::cout << "FPS based on delta: " << 1000.0/delta << std::endl;
	}

	///////////////////////// destroying unregistered entities ////////////////////////////////////
	map<int, BaseEntity*>::iterator mapBegin = GetInstance()->m_entitiesMap.begin();
	map<int, BaseEntity*>::iterator mapEnd   = GetInstance()->m_entitiesMap.end();

	vector<int>::iterator beginOfUnregistered = GetInstance()->m_entitiesToDestroy.begin();
	vector<int>::iterator endOfUnregistered   = GetInstance()->m_entitiesToDestroy.end();

	// Destroy unregistered entities.
	for( beginOfUnregistered; beginOfUnregistered != endOfUnregistered; ++beginOfUnregistered )
	{
		for(mapBegin; mapBegin != mapEnd; ++mapBegin)
		{
			if(mapBegin->second != NULL && (*beginOfUnregistered) == mapBegin->second->getEntityID())
			{
				if(mapBegin->second == GetInstance()->m_pPlayer)
					GetInstance()->setPlayer(NULL);
				//! do not erase element from map to ensure proper mapping of entity id to map element
				delete mapBegin->second;
				mapBegin->second = NULL;
				break;
			}
		}
	}
	GetInstance()->m_entitiesToDestroy.clear();


	/////// Query collisions with level sectors. This step allows drop them and their elements from pipeline. ///////
	vector<LevelSector*>::iterator sectorsStart = m_levelSectors.begin();
	vector<LevelSector*>::iterator sectorsEnd   = m_levelSectors.end();
	ViewFrustum* pCameraFrustum = CameraManager::GetInstance()->GetActiveCamera()->getFrustum();

	for(sectorsStart; sectorsStart != sectorsEnd; ++sectorsStart)
	{
		if((*sectorsStart) == NULL)
			continue;

		if(pCameraFrustum->isInFrustum((*sectorsStart)->getBBox()) == OUTSIDE)
		{
			if((*sectorsStart)->isVisible())
				(*sectorsStart)->changeSectorVisibility(false);
			else
				break;
		}
		else
		{
			if(!(*sectorsStart)->isVisible())
				(*sectorsStart)->changeSectorVisibility(true);
		}
	}

	////////// Frustum test with DYNAMIC entities //////////////////////////////////////////////////////////////////////
	for( mapBegin; mapBegin != mapEnd; ++mapBegin )
	{
		if(mapBegin->second == NULL || m_pPlayer == NULL)
			continue;

		if(mapBegin->second->getType() == DYNAMIC)
		{
			BoundingBox* pBBox = static_cast<Entity3D*>(mapBegin->second)->getBBox();
			if(pCameraFrustum->isInFrustum(pBBox) == OUTSIDE)
				mapBegin->second->setVisibility(false);
			else
				mapBegin->second->setVisibility(true);

			////// Collision test between dynamic entities and player //////////
			if(mapBegin->second->isVisible() && m_pPlayer != mapBegin->second)
			{
				Entity3D* pCollider = static_cast<Entity3D*>(mapBegin->second);
				static_cast<Entity3D*>(m_pPlayer)->queryCollision(pCollider);
			}
		}
	}


	mapBegin = GetInstance()->m_entitiesMap.begin();
	// Update all registered entities.
	for( mapBegin; mapBegin != mapEnd; ++mapBegin )
	{
		if(mapBegin->second == NULL)
			continue;

		if(mapBegin->second->isVisible())
			mapBegin->second->update(delta);
	}

	MVector3D camPos = CameraManager::GetInstance()->GetActiveCamera()->GetPosition();
	MVector3D camTar = CameraManager::GetInstance()->GetActiveCamera()->GetTarget();
	camPos.z -= 0.015f;
	camTar.z -= 0.015f;
	CameraManager::GetInstance()->GetActiveCamera()->SetPosition(camPos);
	CameraManager::GetInstance()->GetActiveCamera()->SetTarget(camTar);

	//cout << CameraManager::GetInstance()->GetActiveCamera()->GetPosition().x << "  " << CameraManager::GetInstance()->GetActiveCamera()->GetPosition().z << endl;

	if(GetInstance()->m_pPlayer)
	{
	MVector3D transl;
	//transl.z = GetInstance()->m_pPlayer->getPosition().z;
	transl.z -= 0.015f;
	GetInstance()->m_pPlayer->translate(transl);
	}

	//! Update emitters
	EmitterManager::GetInstance()->Update(delta);

	ExtensionsLoader::getInstance()->glUseProgramObjectARB(0);
/////////////////////////////////////////////////////////
//! RENDERING PART GOES HERE ////////////////////////////
/////////////////////////////////////////////////////////
	//glViewport(0, 0, 800, 600);
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	/*glMatrixMode(GL_PROJECTION);
	glLoadMatrixd(Globals::GetInstance()->GetLightMatrix(PROJECTION_MATRIX));

	glMatrixMode(GL_MODELVIEW);
	glLoadMatrixd(Globals::GetInstance()->GetLightMatrix(MODELVIEW_MATRIX));
	((ShadowMapFBO*)(ScreenManager::GetInstance()->GetCurrentScreen()))->RenderToFBO(true);

	ExtensionsLoader::getInstance()->glUseProgramObjectARB(ExtensionsLoader::getInstance()->GetShader(GAME_DMAP_GENERATOR));
	glCullFace(GL_FRONT);
	glEnable(GL_CULL_FACE);

	mapBegin = GetInstance()->m_entitiesMap.begin();	
	for( mapBegin; mapBegin != mapEnd; ++mapBegin )
	{
		if(mapBegin->second == NULL)
			continue;

		if(mapBegin->second->isVisible())
			mapBegin->second->render();
	}
	glDisable(GL_CULL_FACE);
	((ShadowMapFBO*)(ScreenManager::GetInstance()->GetCurrentScreen()))->RenderToFBO(false);


	glMultMatrixd( Globals::GetInstance()->GetLightMatrix( MODELVIEW_MATRIX ) );

	MMatrix4 TMat;
	MMatrix4 bias;

	TMat = (bias * Gl

	MMatrix4* invMatrix = new MMatrix4;
	GLdouble* camModelview; camModelview = Globals::GetInstance()->GetCameraMatrix( MODELVIEW_MATRIX );
	invMatrix->InverseMatrix( MMatrix4( camModelview[0], camModelview[1], camModelview[2], camModelview[3], camModelview[4],
		camModelview[5], camModelview[6], camModelview[7], camModelview[8], camModelview[9], camModelview[10], camModelview[11],
		camModelview[12], camModelview[13], camModelview[14], camModelview[15]) );

	ExtensionsLoader::getInstance()->glUseProgramObjectARB(ExtensionsLoader::getInstance()->GetShader(GAME_LIGHTING));
	glBindTexture(GL_TEXTURE_2D, *(Globals::GetInstance()->GetSpecialTexture(SHADOW_MAP)));
	ExtensionsLoader::glActiveTextureARB(GL_TEXTURE1_ARB);
	ExtensionsLoader::glUniformMatrix4fvARB(*(ExtensionsLoader::getInstance()->GetMVInvLoc()), 1, GL_FALSE, invMatrix->m_matrix);	

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);


	glMatrixMode(GL_PROJECTION);
	glLoadMatrixd(Globals::GetInstance()->GetCameraMatrix(PROJECTION_MATRIX));
	glMatrixMode(GL_MODELVIEW);
	glLoadMatrixd(Globals::GetInstance()->GetCameraMatrix(MODELVIEW_MATRIX));*/

	mapBegin = GetInstance()->m_entitiesMap.begin();

	for( mapBegin; mapBegin != mapEnd; ++mapBegin )
	{
		if(mapBegin->second == NULL)
			continue;

		if(mapBegin->second->isVisible())
			mapBegin->second->render();
	}

	
	//! draw particles
	glAlphaFunc(GL_GREATER, 0.0);
	glEnable(GL_ALPHA_TEST);
	EmitterManager::GetInstance()->Render();
	glDisable(GL_ALPHA_TEST);

	glutSwapBuffers();
}

BaseEntity* SceneManager::GetEntityByID(int &entityID)
{
	return m_entitiesMap[entityID];
}

void SceneManager::pushSector(LevelSector* pSector)
{
	m_levelSectors.push_back(pSector);
}

void SceneManager::setPlayer(BaseEntity* pPlayer)
{
	m_pPlayer = pPlayer;
}

void SceneManager::getDynamicIDS(std::vector<int>* pVec)
{
	map<int, BaseEntity*>::iterator currElement = GetInstance()->m_entitiesMap.begin();
	map<int, BaseEntity*>::iterator endElement  = GetInstance()->m_entitiesMap.end();

	for(currElement; currElement != endElement; ++currElement)
	{
		if(currElement->second != NULL && currElement->second->getType() == DYNAMIC)
		{
			pVec->push_back(currElement->second->getEntityID());
		}
	}
}

//EOF
